package com.platform.common.security.interceptor.auth;

import cn.hutool.json.JSONUtil;
import com.platform.common.core.constant.CacheConstants;
import com.platform.common.core.constant.HttpStatus;
import com.platform.common.core.constant.SecurityConstants;
import com.platform.common.core.domain.R;
import com.platform.common.core.exception.ServiceException;
import com.platform.common.core.utils.JwtUtils;
import com.platform.common.core.utils.StringUtils;
import com.platform.common.redis.service.RedisService;
import com.platform.common.security.config.AuthConfigProperties;
import com.platform.common.security.utils.SecurityUtils;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.URLDecoder;
import java.util.Arrays;
import java.util.List;

/**
 * 接口鉴权拦截
 *
 * @author cwp
 * @date 2023-04-07
 */
@Slf4j
@Component
public class AuthInterceptor implements HandlerInterceptor {

    @Resource
    private RedisService redisService;

    @Resource
    private AuthConfigProperties authConfigProperties;

    /***
     * 在请求处理之前进行调用(Controller方法调用之前)
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (!authConfigProperties.isCheck()) {
            return true;
        }
        String allApi = redisService.getCacheObject(CacheConstants.API_ALL_KEY);
        if (StringUtils.isEmpty(allApi)) {
            return true;
        }
        String[] allApis = allApi.split(",");
        List<String> allApiList = Arrays.asList(allApis);
        String uri = request.getRequestURI();
        if (allApiList.contains(uri)) {
            //被拦截校验的 前提————处于安全管理接口管理列表中
            log.debug("当前拦截请求 url = {}", uri);
            // 内部微服务之间的请求：微服务A --> 微服务B
            String innerService = request.getHeader(SecurityConstants.INNER_SERVICE);
            if (StringUtils.isNotEmpty(innerService) && authConfigProperties.isInnerCheck()) {
                String fromService = request.getHeader(SecurityConstants.FROM_SOURCE);
                log.info("请求来源 fromService = {}", fromService);
                if (StringUtils.isEmpty(fromService)) {
                    response.setContentType("application/json;charset=UTF-8");
                    response.getWriter().write(JSONUtil.toJsonStr(R.fail(HttpStatus.BAD_REQUEST, "请求头来源缺失")));
                    log.error(JSONUtil.toJsonStr(R.fail(HttpStatus.BAD_REQUEST, "请求头来源缺失")));
                    return false;
                }
                // 获取应用对应的接口权限列表
                String apis = redisService.getCacheObject(CacheConstants.APP_API_KEY + fromService);
                log.info("应用名: {}, 接口权限列表: {}", fromService, apis);
                if (apis == null) {
                    response.setContentType("application/json;charset=UTF-8");
                    response.getWriter().write(JSONUtil.toJsonStr(R.fail(HttpStatus.UNAUTHORIZED, "接口操作权限不足, 应用名 = " + fromService + ", 操作接口 = " + uri)));
                    log.error("内部接口为空：" + JSONUtil.toJsonStr(R.fail(HttpStatus.UNAUTHORIZED, "接口操作权限不足, 应用名 = " + fromService + ", 操作接口 = " + uri)));
                    return false;
                }
                List<String> apiList = Arrays.asList(apis.split(","));
                if (!apiList.contains(uri)) {
                    response.setContentType("application/json;charset=UTF-8");
                    response.getWriter().write(JSONUtil.toJsonStr(R.fail(HttpStatus.UNAUTHORIZED, "接口操作权限不足, 应用名 = " + fromService + ", 操作接口 = " + uri)));
                    log.error("内部接口不包含：" + JSONUtil.toJsonStr(R.fail(HttpStatus.UNAUTHORIZED, "接口操作权限不足, 应用名 = " + fromService + ", 操作接口 = " + uri)));
                    return false;
                }
            } else if (StringUtils.isEmpty(innerService) && authConfigProperties.isExternalCheck()) { // 外部请求：外部应用 --> 网关 --> 微服务A
                String appKey = request.getHeader(SecurityConstants.APP_KEY);
                String appId = request.getHeader(SecurityConstants.DETAILS_APP_ID);
                String appName = URLDecoder.decode(request.getHeader(SecurityConstants.DETAILS_APP_NAME), "utf-8");
                // 系统请求(admin等其他用户登录的请求)：浏览器 --> 网关 --> 微服务A
                if (StringUtils.isEmpty(appKey) && StringUtils.isEmpty(appId) && StringUtils.isEmpty(appName)) {
                    log.debug("浏览器请求：AppKey = {}, appId = {}, appName = {}", appKey, appId, appName);
                    // 系统请求放行
                    return true;
                }
                log.info("外部请求：AppKey = {}, appId = {}, appName = {}", appKey, appId, appName);
                // 获取应用对应的接口权限列表
                String apis = redisService.getCacheObject(CacheConstants.APP_API_KEY + appName);
                log.info("应用名: {}, 接口权限列表: {}", appName, apis);
                if (apis == null) {
                    response.setContentType("application/json;charset=UTF-8");
                    response.getWriter().write(JSONUtil.toJsonStr(R.fail(HttpStatus.UNAUTHORIZED, "接口操作权限不足, 应用名 = " + appName + ", 操作接口 = " + uri)));
                    log.error("外部接口为空：：" + JSONUtil.toJsonStr(R.fail(HttpStatus.UNAUTHORIZED, "接口操作权限不足, 应用名 = " + appName + ", 操作接口 = " + uri)));

                    return false;
                }
                List<String> apiList = Arrays.asList(apis.split(","));
                if (!apiList.contains(uri)) {
                    response.setContentType("application/json;charset=UTF-8");
                    response.getWriter().write(JSONUtil.toJsonStr(R.fail(HttpStatus.UNAUTHORIZED, "接口操作权限不足, 应用名 = " + appName + ", 操作接口 = " + uri)));
                    log.error("外部接口不包含：：" + JSONUtil.toJsonStr(R.fail(HttpStatus.UNAUTHORIZED, "接口操作权限不足, 应用名 = " + appName + ", 操作接口 = " + uri)));

                    return false;
                }
            } else {
                return true;
            }
        }
        // 如果设置为false时，被请求时，拦截器执行到此处将不会继续操作
        // 如果设置为true时，请求将会继续执行后面的操作
        return true;
    }

    /***
     * 请求处理之后进行调用，但是在视图被渲染之前（Controller方法调用之后）
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    /***
     * 整个请求结束之后被调用，也就是在DispatchServlet渲染了对应的视图之后执行（主要用于进行资源清理工作）
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}
